package com.huxi.http;

import static com.huxi.http.HttpUtils.genheaders;
import static com.huxi.http.HttpUtils.genurl;
import static com.huxi.http.HttpUtils.trancTextLength;
import static org.apache.commons.lang3.StringUtils.isBlank;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;

public class Client {

	/**
	 * 创建RestTemplate实例
	 * 
	 * @param connectTimeout 连接超时，单位毫秒。
	 * @param readTimeout    响应超时，单位毫秒。
	 * @return 实例
	 */
	public static RestTemplate createRestTemplate(int connectTimeout, int readTimeout, Charset charset) {
		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
		requestFactory.setConnectTimeout(connectTimeout);
		requestFactory.setReadTimeout(readTimeout);
		return createRestTemplate(requestFactory, StandardCharsets.UTF_8);
	}

	public static RestTemplate createRestTemplate(ClientHttpRequestFactory factory, Charset charset) {
		RestTemplate restTemplate = new RestTemplate(Objects.requireNonNull(factory, "HTTP请求工厂参数不能为空值"));
		for (HttpMessageConverter<?> mc : restTemplate.getMessageConverters()) {
			if (mc instanceof StringHttpMessageConverter) {
				((StringHttpMessageConverter) mc).setDefaultCharset(charset != null ? charset : StandardCharsets.UTF_8);
			}
		}
		return restTemplate;
	}

	protected final static Logger logger = LoggerFactory.getLogger(Client.class);
	private RestTemplate restTemplate; // 连接模板
	private int responseLogLength = 256; // HTTP响应内容日志长度
	private ObjectMapper mapper = new ObjectMapper();

	public Client(RestTemplate restTemplate) {
		this.restTemplate = Objects.requireNonNull(restTemplate);
	}

	public Client() {
		this(createRestTemplate(new SimpleClientHttpRequestFactory(), StandardCharsets.UTF_8));
	}

	public Client(int connectTimeout, int readTimeout) {
		this(createRestTemplate(connectTimeout, readTimeout, StandardCharsets.UTF_8));
	}

	/**
	 * 文本类响应内容HTTP请求处理
	 * 
	 * @param path    请求路劲模板，非空值。
	 * @param pvars   请求路劲变量，非空值。
	 * @param method  请求方法
	 * @param headers 请求头
	 * @param body    请求参数
	 * @return 请求结果，非空值、空串。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public String exchange(String path, Map<String, String> pvars, HttpMethod method, Map<String, ?> headers,
			String body) throws HttpCallException {
		final String murl = genurl(path, pvars);
		return exchange(murl, genheaders(headers), body, method);
	}

	/**
	 * 文本类响应内容HTTP请求处理
	 * 
	 * @param url     请求地址，非空值。
	 * @param method  请求方法
	 * @param headers 请求头
	 * @param body    请求参数
	 * @return 请求结果，非空值、空串。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public String exchange(String url, HttpMethod method, Map<String, ?> headers, String body)
			throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchange(url, genheaders(headers), body, method);
	}

	/**
	 * 文本类响应内容HTTP请求处理
	 * 
	 * @param url    请求地址，非空值。
	 * @param method 请求方法
	 * @param body   请求参数
	 * @return 请求结果，非空值、空串。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public String exchange(String url, HttpMethod method, String body) throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchange(url, null, body, method);
	}

	/**
	 * 文本类响应内容HTTP请求处理
	 * 
	 * @param url       请求地址，非空值。
	 * @param method    请求方法
	 * @param body      请求参数
	 * @param headersFn 请求头生成函数
	 * @return 请求结果，非空值、空串。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public String exchange(String url, HttpMethod method, String body, Function<HttpMethod, HttpHeaders> headersFn)
			throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchange(url, genFnHeaders(headersFn, url, method, body), body, method);
	}

	/**
	 * 文本类响应内容HTTP请求处理
	 * 
	 * @param url     请求地址，非空值。
	 * @param headers 请求头
	 * @param body    请求参数
	 * @param method  请求方法
	 * @return 请求结果，非空值、空串。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public String exchange(String url, HttpHeaders headers, String body, HttpMethod method) throws HttpCallException {
		return exchangeHttp(url, headers, body, method, (ResponseEntity<String> rs) -> {
			return rs.getBody();
		});
	}

	/**
	 * 文本类响应内容HTTP请求处理
	 * 
	 * @param <T>     结果类型
	 * @param url     请求地址，非空值。
	 * @param headers 请求头
	 * @param body    请求参数
	 * @param method  请求方法
	 * @param parse   响应内容解析器，非空值。
	 * @return 请求结果
	 * @throws HttpCallException 请求发送接收失败
	 * @throws RuntimeException  响应内容处理失败
	 */
	protected <T> T exchangeHttp(final String url, HttpHeaders headers, String body, HttpMethod method,
			Function<ResponseEntity<String>, T> parse) throws HttpCallException {
		Objects.requireNonNull(parse, "HTTP访问响应内容解析器不能为空值");
		final long tm = System.currentTimeMillis();
		method = autoChoiceMethod(method, body);
		String crul = url;
		ResponseEntity<String> response = null;
		String rs = null;
		try {
			if (HttpMethod.GET.equals(method) && !isBlank(body)) {
				crul += crul.contains("?") ? "&" : "?";
				crul += body;
				body = "";
			}
			logger.info("HTTP访问请求内容 [{}] & [{}] & {}", method, crul, body);
			if (headers != null && headers.size() != 0) {
				logger.debug("HTTP访问请求头 - {}", HttpHeaders.formatHeaders(headers));
			}

			HttpEntity<String> entity = new HttpEntity<>(body, headers);
			response = restTemplate.exchange(crul, method, entity, String.class);
			rs = response.getBody();
		} catch (RestClientException ee) {
			logger.error(MessageFormat.format("HTTP访问失败 - {0}", crul), ee);
			throw new HttpCallException("HTTP访问失败", ee).withUrl(crul).withRequestBody(body);
		} catch (HttpCallException ee) {
			logger.error(MessageFormat.format("HTTP访问前失败 - {0}", "访问路径无效。"), ee);
			throw ee.withRequestBody(body);
		} catch (Throwable ee) {
			logger.error(MessageFormat.format("HTTP响应失败 - {0}", crul), ee);
			throw new HttpCallException("HTTP响应失败", ee).withUrl(crul).withRequestBody(body);
		}

		if (rs == null || rs.length() == 0) {
			logger.error(MessageFormat.format("HTTP响应失败，内容为空 - {0}", crul));
			throw new HttpCallException("HTTP响应内容为空").withUrl(crul).withRequestBody(body).withResponseBody("");
		}

		logger.info("HTTP访问响应结束 - 耗时{}毫秒，响应内容{}字节。", System.currentTimeMillis() - tm, rs.length());

		if (logger.isDebugEnabled()) {
			logger.debug("HTTP访问响应内容 - {}", trancTextLength(rs, responseLogLength, " ..."));
		}
		try {
			return parse.apply(response);
		} catch (HttpCallException ee) {
			logger.error(MessageFormat.format("HTTP响应内容解析失败 - {0}", crul), ee);
			throw ee.withUrl(crul).withRequestBody(body).withResponseBody(rs);
		} catch (RuntimeException ee) {
			logger.error(MessageFormat.format("HTTP响应内容处理失败 - {0}", crul), ee);
			throw ee;
		}
	}

	/**
	 * 文本类响应内容HTTP请求且解析
	 * 
	 * @param <T>     结果类型
	 * @param url     请求地址，非空值。
	 * @param headers 请求头
	 * @param body    请求参数
	 * @param method  请求方法
	 * @param jsonp   是否跨域JSONP协议
	 * @param parser  响应内容解析器，非空值。
	 * @return 响应内容解析器处理后结果
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> T exchangeAndParse(String url, HttpHeaders headers, String body, HttpMethod method, boolean jsonp,
			Function<String, T> parser) throws HttpCallException {
		Objects.requireNonNull(parser, "响应内容解析器不能为空值");
		return exchangeHttp(url, headers, body, method, (ResponseEntity<String> rs) -> {
			String ss = rs.getBody();
			if (jsonp) {
				// 跨域请求jsonp协议
				ss = ss.trim();
				int l = ss.length() - 1;
				int b = ss.indexOf('{');
				if (b > 0 && ss.charAt(l) == ')') {
					ss = ss.substring(b, l);
				}
			}
			return parser.apply(ss);
		});
	}

	/**
	 * 文本类响应内容HTTP请求处理且解析
	 * 
	 * @param <T>     结果类型
	 * @param url     请求地址，非空值。
	 * @param method  请求方法
	 * @param body    请求参数
	 * @param headers 请求头
	 * @param jsonp   是否跨域JSONP协议
	 * @param parser  响应内容解析器，非空值。
	 * @return 响应内容解析器处理后结果
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> T exchangeAndParse(String url, HttpMethod method, String body, Map<String, ?> headers, boolean jsonp,
			Function<String, T> parser) throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchangeAndParse(url, genheaders(headers), body, method, jsonp, parser);
	}

	/**
	 * 文本类响应内容HTTP请求处理且解析
	 * 
	 * @param <T>    结果类型
	 * @param url    请求地址，非空值。
	 * @param method 请求方法
	 * @param body   请求参数
	 * @param jsonp  是否跨域JSONP协议
	 * @param parser 响应内容解析器，非空值。
	 * @return 响应内容解析器处理后结果
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> T exchangeAndParse(String url, HttpMethod method, String body, boolean jsonp, Function<String, T> parser)
			throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchangeAndParse(url, null, body, method, jsonp, parser);
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回哈希表对象
	 * 
	 * @param url     请求地址，非空值。
	 * @param headers 请求头
	 * @param body    请求参数
	 * @param method  请求方法
	 * @param jsonp   是否跨域JSONP协议
	 * @return 哈希表对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public Map<String, Object> exchangeForMap(String url, HttpHeaders headers, String body, HttpMethod method,
			boolean jsonp) throws HttpCallException {
		return exchangeAndParse(url, headers, body, method, jsonp, (rs) -> {
			try {
				TypeFactory typeFactory = TypeFactory.defaultInstance();
				JavaType javaType = typeFactory.constructMapType(Map.class, String.class, Object.class);
				Map<String, Object> ro = mapper.readValue(rs, javaType);
				return ro;
			} catch (Exception en) {
				throw new RuntimeException("HTTP响应内容解析JSONObject对象失败: Map<String,Object>", en);
			}
		});
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回哈希表对象
	 * 
	 * @param url     请求地址，非空值。
	 * @param method  请求方法
	 * @param body    请求参数
	 * @param headers 请求头
	 * @param jsonp   是否跨域JSONP协议
	 * @return 哈希表对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public Map<String, Object> exchangeForMap(final String url, HttpMethod method, String body, Map<String, ?> headers,
			boolean jsonp) throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchangeForMap(url, genheaders(headers), body, method, jsonp);
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回哈希表对象
	 * 
	 * @param url    请求地址，非空值。
	 * @param method 请求方法
	 * @param body   请求参数
	 * @param jsonp  是否跨域JSONP协议
	 * @return 哈希表对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public Map<String, Object> exchangeForMap(final String url, HttpMethod method, String body, boolean jsonp)
			throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchangeForMap(url, null, body, method, jsonp);
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回数组对象
	 * 
	 * @param <T>         数组元素类型
	 * @param url         请求地址，非空值。
	 * @param headers     请求头
	 * @param body        请求参数
	 * @param method      请求方法
	 * @param jsonp       是否跨域JSONP协议
	 * @param elementType 数组元素类型，非空值。
	 * @return 数组对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> List<T> exchangeForList(String url, HttpHeaders headers, String body, HttpMethod method, boolean jsonp,
			Class<T> elementType) throws HttpCallException {
		Objects.requireNonNull(elementType, "数组元素类型不能为空值");
		return exchangeAndParse(url, headers, body, method, jsonp, (rs) -> {
			try {
				TypeFactory typeFactory = TypeFactory.defaultInstance();
				JavaType javaType = typeFactory.constructCollectionType(ArrayList.class, elementType);
				List<T> mlist = mapper.readValue(rs, javaType);
				return mlist;
			} catch (Exception en) {
				throw new RuntimeException("HTTP响应内容解析JSONObject对象失败: Map<String,Object>", en);
			}
		});
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回数组对象
	 * 
	 * @param <T>         数组元素类型
	 * @param url         请求地址，非空值。
	 * @param method      请求方法
	 * @param body        请求参数
	 * @param headers     请求头
	 * @param jsonp       是否跨域JSONP协议
	 * @param elementType 数组元素类型，非空值。
	 * @return 数组对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> List<T> exchangeForList(final String url, HttpMethod method, String body, Map<String, ?> headers,
			boolean jsonp, Class<T> elementType) throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchangeForList(url, genheaders(headers), body, method, jsonp, elementType);
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回数组对象
	 * 
	 * @param <T>         数组元素类型
	 * @param url         请求地址，非空值。
	 * @param method      请求方法
	 * @param body        请求参数
	 * @param headers     请求头
	 * @param jsonp       是否跨域JSONP协议
	 * @param elementType 数组元素类型，非空值。
	 * @return 数组对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> List<T> exchangeForList(final String url, HttpMethod method, String body, boolean jsonp,
			Class<T> elementType) throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchangeForList(url, null, body, method, jsonp, elementType);
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回指定类型对象
	 * 
	 * @param <T>        指定类型
	 * @param url        请求地址，非空值。
	 * @param headers    请求头
	 * @param body       请求参数
	 * @param method     请求方法
	 * @param jsonp      是否跨域JSONP协议
	 * @param resultType 指定类型，非空值。
	 * @return 指定类型对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> T exchangeForObject(String url, HttpHeaders headers, String body, HttpMethod method, boolean jsonp,
			Class<T> resultType) throws HttpCallException {
		Objects.requireNonNull(resultType, "返回类型不能为空值");
		return exchangeAndParse(url, headers, body, method, jsonp, (rs) -> {
			try {
				return mapper.readValue(rs, resultType);
			} catch (Exception en) {
				throw new RuntimeException("HTTP响应内容解析JSONObject对象失败: Map<String,Object>", en);
			}
		});
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回指定类型对象
	 * 
	 * @param <T>        指定类型
	 * @param url        请求地址，非空值。
	 * @param method     请求方法
	 * @param body       请求参数
	 * @param headers    请求头
	 * @param jsonp      是否跨域JSONP协议
	 * @param resultType 指定类型，非空值。
	 * @return 指定类型对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> T exchangeForObject(final String url, HttpMethod method, String body, Map<String, ?> headers,
			boolean jsonp, Class<T> resultType) throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchangeForObject(url, genheaders(headers), body, method, jsonp, resultType);
	}

	/**
	 * 文本类响应内容HTTP请求处理，返回指定类型对象
	 * 
	 * @param <T>        指定类型
	 * @param url        请求地址，非空值。
	 * @param method     请求方法
	 * @param body       请求参数
	 * @param jsonp      是否跨域JSONP协议
	 * @param resultType 指定类型，非空值。
	 * @return 指定类型对象，非空值。
	 * @throws HttpCallException 请求发送接收失败
	 */
	public <T> T exchangeForObject(final String url, HttpMethod method, String body, boolean jsonp, Class<T> resultType)
			throws HttpCallException {
		method = autoChoiceMethod(method, body);
		return exchangeForObject(url, null, body, method, jsonp, resultType);
	}

	private HttpMethod autoChoiceMethod(HttpMethod method, String body) {
		return method != null ? method : isBlank(body) ? HttpMethod.GET : HttpMethod.POST;

	}

	private HttpHeaders genFnHeaders(Function<HttpMethod, HttpHeaders> headersFn, String url, HttpMethod method,
			String body) throws HttpCallException {
		if (headersFn != null) {
			try {
				return headersFn.apply(method);
			} catch (Exception en) {
				logger.error(MessageFormat.format("HTTP请求头生成失败 - {0}", url), en);
				throw new HttpCallException("HTTP请求头生成失败", en).withUrl(url).withRequestBody(body);
			}
		}
		return null;
	}

	/**
	 * 文件下载处理
	 * 
	 * @param url      下载地址，非空。
	 * @param headers  请求头
	 * @param body     请求参数
	 * @param method   请求方法
	 * @param consumer 文件流处理方法，非空值。
	 * @throws HttpCallException 文件内容下载失败
	 * @throws RuntimeException  文件内容处理失败
	 */
	public <T> T download(final String url, HttpHeaders headers, String body, HttpMethod method,
			BiFunction<InputStream, ResponseEntity<?>, T> consumer) throws HttpCallException, RuntimeException {
		Objects.requireNonNull(consumer, "文件流处理方法不能为空值");
		final long tm = System.currentTimeMillis();
		String crul = url;
		method = autoChoiceMethod(method, body);
		if (HttpMethod.GET.equals(method) && !isBlank(body)) {
			crul += crul.contains("?") ? "&" : "?";
			crul += body;
			body = "";
		}
		ResponseEntity<Resource> response = null;
		Resource rs = null;
		T ro = null;
		try {
			if (isBlank(url)) {
				throw new HttpUrlException();
			}
			logger.info("HTTP下载请求内容 [{}] & [{}] & {}", method, crul, body);
			if (headers != null && headers.size() != 0) {
				logger.debug("HTTP下载请求头 - {}", HttpHeaders.formatHeaders(headers));
			}
			HttpEntity<String> entity = new HttpEntity<>(body, headers);
			response = restTemplate.exchange(crul, method, entity, Resource.class);
			rs = response.getBody();
		} catch (RestClientException ee) {
			logger.error(MessageFormat.format("HTTP下载失败 - {0}", crul), ee);
			throw new HttpCallException("HTTP下载失败", ee).withUrl(crul).withRequestBody(body);
		} catch (HttpUrlException ee) {
			logger.error(MessageFormat.format("HTTP下载失败 - {0}", "访问地址不能为空"), ee);
			throw ee.withRequestBody(body);
		} catch (Throwable ee) {
			logger.error(MessageFormat.format("HTTP下载失败 - {0}", crul), ee);
			throw new HttpCallException("HTTP下载失败", ee).withUrl(crul).withRequestBody(body);
		}

		logger.info("HTTP下载开始接收 - 耗时{}毫秒，文件名称 - ", System.currentTimeMillis() - tm);

		try (InputStream input = rs.getInputStream()) {
			ro = consumer.apply(input, response);
		} catch (IOException en) {
			logger.error(MessageFormat.format("HTTP下载失败，文件流创建失败 - {0}", crul), en);
			throw new HttpCallException("HTTP下载失败", en).withUrl(crul).withRequestBody(body);
		} catch (HttpCallException en) {
			logger.error(MessageFormat.format("HTTP下载失败，文件流接收失败 - {0}", crul), en);
			throw en.withUrl(crul).withRequestBody(body);
		} catch (RuntimeException en) {
			// 下载内容解析失败
			logger.error("HTTP下载失败，文件流处理失败 - {0}", crul);
			throw en;
		}
		logger.info("HTTP下载处理结束 - 耗时{}毫秒。", System.currentTimeMillis() - tm);
		return ro;
	}

	/**
	 * 文件下载处理
	 * 
	 * @param url      下载地址，非空。
	 * @param headers  请求头
	 * @param body     请求参数
	 * @param method   请求方法
	 * @param consumer 文件流处理方法，非空值。
	 * @throws HttpCallException 文件内容下载失败
	 * @throws RuntimeException  文件内容处理失败
	 */
	public <T> T downloadFile(final String url, HttpHeaders headers, String body, HttpMethod method,
			final Function<InputStream, T> consumer) throws HttpCallException, RuntimeException {
		Objects.requireNonNull(consumer, "文件流处理方法不能为空值");
		return download(url, headers, body, method, (stream, response) -> {
			return consumer.apply(stream);
		});
	}

	/**
	 * 文件下载处理
	 * 
	 * @param url       下载地址，非空。
	 * @param method    请求方法
	 * @param body      请求参数
	 * @param consumer  文件流处理方法，非空值。
	 * @param headersFn 请求头生成函数
	 * @throws HttpCallException 文件内容下载失败
	 * @throws RuntimeException  文件内容处理失败
	 */
	public <T> T download(final String url, HttpMethod method, String body,
			BiFunction<InputStream, ResponseEntity<?>, T> consumer, Function<HttpMethod, HttpHeaders> headersFn)
			throws HttpCallException, RuntimeException {
		method = autoChoiceMethod(method, body);
		HttpHeaders mheaders = genFnHeaders(headersFn, url, method, body);
		return download(url, mheaders, body, method, consumer);
	}

	/**
	 * 文件下载处理
	 * 
	 * @param url       下载地址，非空。
	 * @param method    请求方法
	 * @param body      请求参数
	 * @param consumer  文件流处理方法，非空值。
	 * @param headersFn 请求头生成函数
	 * @throws HttpCallException 文件内容下载失败
	 * @throws RuntimeException  文件内容处理失败
	 */
	public <T> T downloadFile(final String url, HttpMethod method, String body, Function<InputStream, T> consumer,
			Function<HttpMethod, HttpHeaders> headersFn) throws HttpCallException, RuntimeException {
		method = autoChoiceMethod(method, body);
		HttpHeaders mheaders = genFnHeaders(headersFn, url, method, body);
		return downloadFile(url, mheaders, body, method, consumer);
	}

	/**
	 * 文件下载处理
	 * 
	 * @param url      下载地址，非空。
	 * @param method   请求方法
	 * @param body     请求参数
	 * @param headers  请求头
	 * @param consumer 文件流处理方法，非空值。
	 * @throws HttpCallException 文件内容下载失败
	 * @throws RuntimeException  文件内容处理失败
	 */
	public <T> T download(final String url, HttpMethod method, String body, Map<String, ?> headers,
			BiFunction<InputStream, ResponseEntity<?>, T> consumer) throws HttpCallException, RuntimeException {
		method = autoChoiceMethod(method, body);
		HttpHeaders mheaders = genheaders(headers);
		return download(url, mheaders, body, method, consumer);
	}

	/**
	 * 文件下载处理
	 * 
	 * @param url      下载地址，非空。
	 * @param method   请求方法
	 * @param body     请求参数
	 * @param headers  请求头
	 * @param consumer 文件流处理方法，非空值。
	 * @throws HttpCallException 文件内容下载失败
	 * @throws RuntimeException  文件内容处理失败
	 */
	public <T> T downloadFile(final String url, HttpMethod method, String body, Map<String, ?> headers,
			Function<InputStream, T> consumer) throws HttpCallException, RuntimeException {
		method = autoChoiceMethod(method, body);
		HttpHeaders mheaders = genheaders(headers);
		return downloadFile(url, mheaders, body, method, consumer);
	}

}
